﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;

namespace sp_hw1
{
    class Program
    {

        static void Main(string[] args)
        {
            int iLocctr;
            string sLocctr;
            string sLoc;
            DataTable dtOPTAB;
            DataTable dtSYMTAB;
            DataTable dtResult;

            bool bPass1 = true;
            bool bPass2 = true;
            bool bCommentLine = false;
            string sLabel = "";
            string sOpcode = "";
            string sOperand = "";
            string sTemp = "";

            #region Init dtResult
            dtResult = new DataTable();
            dtResult.Columns.Add("Loc");
            dtResult.Columns.Add("Label");
            dtResult.Columns.Add("OpCode");
            dtResult.Columns.Add("Operand");
            dtResult.Columns.Add("ObjectCode");
            #endregion

            #region Init SYMTAB
            dtSYMTAB = new DataTable();
            dtSYMTAB.Columns.Add("Label");
            dtSYMTAB.Columns.Add("Address");
            #endregion

            #region Init OPTAB
            dtOPTAB = new DataTable();
            dtOPTAB.Columns.Add("Mnemonic");
            dtOPTAB.Columns.Add("OPCODE");
            dtOPTAB.Columns.Add("Format");
            using (TextReader tr = File.OpenText("..\\..\\OPTAB.txt"))
            {
                string sLine = string.Empty;
                string[] aryColumns = null;
                while (((sLine = tr.ReadLine()) != null) && bPass1)
                {
                    string sMnemonic;
                    string sOPCODE;
                    string sFormat;

                    aryColumns = sLine.Split('\t');
                    if (aryColumns.Length >= 3)
                    {
                        sMnemonic = aryColumns[0].ToString();
                        sOPCODE = aryColumns[1].ToString();
                        sFormat = aryColumns[2].ToString();

                        DataRow dr = dtOPTAB.NewRow();
                        dr["Mnemonic"] = sMnemonic;
                        dr["OPCODE"] = sOPCODE;
                        dr["Format"] = sFormat;
                        dtOPTAB.Rows.Add(dr);
                    }
                }
                tr.Close();
            }

            /*
            foreach(DataRow dr in dtOPTAB.Rows)
            {
                Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString() + "\t" + dr[2].ToString());
            }
            */
            #endregion            


            #region Pass 1
            using (TextReader tr = File.OpenText("..\\..\\hw.txt"))
            {
                string sLine = string.Empty;
                string[] aryColumns = null;

                sLine = tr.ReadLine();

                aryColumns = sLine.Split('\t');
                if (aryColumns.Length >= 3)
                {
                    sLabel = aryColumns[0].ToString();
                    sOpcode = aryColumns[1].ToString();
                    sOperand = aryColumns[2].ToString();                    
                }

                if (sOpcode == "START")
                {
                    sLocctr = sOperand;
                    sLoc = sLocctr;

                    DataRow drResult = dtResult.NewRow();
                    drResult["Loc"] = sLocctr;
                    drResult["Label"] = sLabel;
                    drResult["OpCode"] = sOpcode;
                    drResult["Operand"] = sOperand;
                    drResult["ObjectCode"] = "";
                    dtResult.Rows.Add(drResult);


                    sLine = tr.ReadLine();
                    aryColumns = sLine.Split('\t');
                    if (aryColumns.Length >= 3)
                    {
                        sLabel = aryColumns[0].ToString();
                        sOpcode = aryColumns[1].ToString();
                        sOperand = aryColumns[2].ToString();
                    }

                    while (sOpcode != "END")
                    {
                        if (! bCommentLine)
                        {
                            if (sLabel.Length > 0)
                            {
                                bool bFindSymbol = false;
                                foreach (DataRow dr in dtSYMTAB.Rows)
                                {
                                    sTemp = "";
                                    sTemp = dr["Label"].ToString();

                                    if (sLabel == sTemp)
                                    {
                                        bFindSymbol = true;
                                        break;
                                    }
                                }

                                if (bFindSymbol)
                                {
                                    Console.WriteLine("Symbol Table:");
                                    foreach (DataRow dr in dtSYMTAB.Rows)
                                    {
                                        Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString());
                                    }
                                    Console.WriteLine("");
                                    Console.WriteLine("Result Table:");
                                    foreach (DataRow dr in dtResult.Rows)
                                    {
                                        Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString() + "\t" + dr[2].ToString() + "\t" + dr[3].ToString());
                                    }
                                    Console.WriteLine("Duplicate symbol ! " + sLabel);
                                    return;
                                }
                                else
                                {
                                    DataRow dr = dtSYMTAB.NewRow();
                                    dr["Label"] = sLabel;
                                    dr["Address"] = sLocctr;
                                    dtSYMTAB.Rows.Add(dr);
                                }

                                //Search OPCODE     
                                bool bFindOpCode = false;
                                foreach (DataRow dr in dtOPTAB.Rows)
                                {
                                    sTemp = "";
                                    sTemp = dr["Mnemonic"].ToString();

                                    if (sTemp == sOpcode || sOpcode == "WORD" || sOpcode == "RESW" || sOpcode == "RESB" || sOpcode == "BYTE")
                                    {
                                        bFindOpCode = true;
                                        break;
                                    }
                                }
                                if (bFindOpCode)
                                {
                                    iLocctr = Convert.ToInt32(sLocctr, 16);
                                    switch (sOpcode)
                                    {
                                        case "WORD":
                                            iLocctr = iLocctr + 3;
                                            break;
                                        case "RESW":
                                            iLocctr = iLocctr + (3 * Convert.ToInt16(sOperand));
                                            break;
                                        case "RESB":
                                            iLocctr = iLocctr + (Convert.ToInt16(sOperand));
                                            break;
                                        case "BYTE":
                                            string[] aryTemp = sOperand.Split('\'');
                                            if (aryTemp[0] == "C")
                                            {
                                                iLocctr = iLocctr + aryTemp[1].Length;
                                            }
                                            else 
                                            {
                                                iLocctr = iLocctr + 1;
                                            }
                                            break;
                                        default:
                                            iLocctr = iLocctr + 3;
                                            break;
                                    }
                                    sLocctr = iLocctr.ToString("X");
                                }
                                else
                                {
                                    Console.WriteLine("Invalid operation Code! " + sOpcode);
                                    return;
                                }
                            }
                            else
                            {
                                iLocctr = Convert.ToInt32(sLocctr, 16);
                                iLocctr = iLocctr + 3;
                                sLocctr = iLocctr.ToString("X");
                            }
                        }

                        drResult = dtResult.NewRow();
                        drResult["Loc"] = sLoc;
                        drResult["Label"] = sLabel;
                        drResult["OpCode"] = sOpcode;
                        drResult["Operand"] = sOperand;
                        drResult["ObjectCode"] = "";
                        dtResult.Rows.Add(drResult);

                        sLoc = sLocctr;

                        //Read Next Line
                        sLine = tr.ReadLine();
                        aryColumns = sLine.Split('\t');
                        if (aryColumns.Length >= 3)
                        {
                            sLabel = aryColumns[0].ToString();
                            sOpcode = aryColumns[1].ToString();
                            sOperand = aryColumns[2].ToString();
                        }
                    }

                    drResult = dtResult.NewRow();
                    drResult["Loc"] = "";
                    drResult["Label"] = sLabel;
                    drResult["OpCode"] = sOpcode;
                    drResult["Operand"] = sOperand;
                    drResult["ObjectCode"] = "";
                    dtResult.Rows.Add(drResult);
                }               
                tr.Close();
            }

            /*
            Console.WriteLine("Loc \tSource Statement \tObject Code");
            foreach (DataRow dr in dtResult.Rows)
            {
                Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString() + "\t" + dr[2].ToString() + "\t" + dr[3].ToString());
            }

            Console.WriteLine("Symbol Table:");
            foreach (DataRow dr in dtSYMTAB.Rows)
            {
                Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString());
            }
            Console.ReadLine();
            */
            #endregion
            
            #region Pass 2
            using (TextReader tr = File.OpenText("..\\..\\hw.txt"))
            {   
                string sLine = string.Empty;
                string[] aryColumns = null;

                sLine = tr.ReadLine();

                aryColumns = sLine.Split('\t');
                if (aryColumns.Length >= 3)
                {
                    sLabel = aryColumns[0].ToString();
                    sOpcode = aryColumns[1].ToString();
                    sOperand = aryColumns[2].ToString();                    
                }

                string sOpAddress = "";
                string sAddress = "";
                int iRow = 0;
                bool bRegX = false;
                if (sOpcode == "START")
                {
                    DataRow drResult = dtResult.Rows[iRow];
                    drResult["ObjectCode"] = "";                   

                    //Read Next 
                    sLine = tr.ReadLine();
                    aryColumns = sLine.Split('\t');
                    if (aryColumns.Length >= 3)
                    {
                        sLabel = aryColumns[0].ToString();
                        sOpcode = aryColumns[1].ToString();
                        sOperand = aryColumns[2].ToString();
                    }

                    while (sOpcode != "END")
                    {
                        iRow++;
                        sAddress = "";
                        sOpAddress = "";
                        if (!bCommentLine)
                        {
                            //Search OPTAB
                            bool bFindOpCode = false;
                            bRegX = false;
                            foreach (DataRow dr in dtOPTAB.Rows)
                            {
                                sTemp = "";
                                sTemp = dr["Mnemonic"].ToString();
                                if (sTemp == sOpcode )
                                {
                                    bFindOpCode = true;
                                    sOpAddress = dr["OPCODE"].ToString();
                                    break;
                                }
                            }

                            if (bFindOpCode)
                            {
                                if (sOperand.Length > 0)
                                {
                                    bool bFindSymbol = false;
                                    foreach (DataRow dr in dtSYMTAB.Rows)
                                    {
                                        sTemp = "";
                                        sTemp = dr["Label"].ToString();

                                        string[] aryTemp = sOperand.Split(',');
                                        sOperand = aryTemp[0];

                                        if (aryTemp.Length > 1)
                                        {
                                            if (aryTemp[1] == "X")
                                            {
                                                bRegX = true;
                                            }
                                        }
                                        if (sOperand == sTemp)
                                        {
                                            bFindSymbol = true;
                                            sAddress = dr["Address"].ToString();
                                            break;
                                        }
                                       
                                    }

                                    if (! bFindSymbol)
                                    {                                    
                                        Console.WriteLine("undefined symbol! " + sOperand);
                                        return;
                                    }
                                }
                                else 
                                {
                                    sAddress = "0000";
                                }
                            }
                            else if (sOpcode == "BYTE" )
                            {
                                string[] aryTemp = sOperand.Split('\'');
                                if (aryTemp[0] == "C")
                                {
                                   //to ASCII
                                   //sAddress 
                                    foreach (Char C in aryTemp[1])
                                    {
                                        int N = Convert.ToInt32(C);
                                        sAddress = sAddress + N.ToString("X");
                                    }
                                    sOpAddress = "";
                                    //Console.WriteLine(sAddress);
                                }
                                else
                                {
                                    sAddress = aryTemp[1];
                                }
                            }
                            else if ( sOpcode =="WORD")
                            {
                                int iOperand = Convert.ToInt32(sOperand);
                                sTemp = iOperand.ToString("X");

                                for (int i = sTemp.Length; i < 6; i++)
                                {
                                    sTemp = "0" + sTemp;
                                }

                                sAddress = sTemp;
                                sOpAddress = "";
                                //Console.WriteLine(sAddress);
                            }
                        }

                        
                        if (bRegX)
                        {
                            int x = Convert.ToInt32(sAddress.Substring(0, 1));
                            x = x + 8;
                            string X = x.ToString("X");


                            sAddress = X + sAddress.Substring(1, sAddress.Length -1);
                        }             
           
                        drResult = dtResult.Rows[iRow];
                        drResult["ObjectCode"] = sOpAddress + sAddress;        

                        //Read Next 
                        sLine = tr.ReadLine();
                        aryColumns = sLine.Split('\t');
                        if (aryColumns.Length >= 3)
                        {
                            sLabel = aryColumns[0].ToString();
                            sOpcode = aryColumns[1].ToString();
                            sOperand = aryColumns[2].ToString();
                        }
                    }
                }
                tr.Close();
            }
            
            Console.WriteLine("Loc \tSource Statement \t\tObject Code");
            foreach (DataRow dr in dtResult.Rows)
            {
                Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString() + "\t" + dr[2].ToString() + "\t" + dr[3].ToString() + "\t\t" + dr[4].ToString());
            }

            Console.WriteLine("Symbol Table:");
            foreach (DataRow dr in dtSYMTAB.Rows)
            {
                Console.WriteLine(dr[0].ToString() + "\t" + dr[1].ToString());
            }
            Console.ReadLine();
            
            #endregion 
            Console.ReadLine();

        }
    }
}
